#include "IndiceBSharp.h"
#include "DatoBSharp.h"
#include "Archivo.h"
#include "Buffer.h"
#include "ControladorAlmacenamientoBSharp.h"
#include "AdministradorBufferingBSharp.h"
#include "WrapperBufferBSharp.h"
#include "funciones.h"
#include <algorithm>
#include <list>

namespace Toolkit
{

IndiceBSharp::IndiceBSharp(const unsigned long int tamanoComponente,
        const string &ruta, const string &nombre,
        const TipoSerializacion &tSerializacion)
{

    this-> tSerializacion = tSerializacion;
    string rutaNueva = ruta + "indiceBSharp/";

    WrapperComponente wrapperVacio;
    WrapperBufferBSharp wrapperBuffer(0);

    stringstream metadata;
    serializacionDigito(metadata, tSerializacion, tamanoComponente);
    serializacionDigito(metadata, tSerializacion, tamanoComponente); //guarda espacio para  la altura

    primario = new Archivo(tamanoComponente + wrapperVacio.getTamanoMetadata(
            tSerializacion), rutaNueva, nombre, tSerializacion, metadata, (4
            * tamanoComponente) / 3 + 1 + wrapperVacio.getTamanoMetadata(
            tSerializacion));

    secundario = new Buffer(tamanoComponente + OFFSET_BUFFER
            + wrapperBuffer.getTamanoMetadata(tSerializacion), tSerializacion,
            (4 * tamanoComponente) / 3 + 1 + OFFSET_BUFFER
                    + wrapperBuffer.getTamanoMetadata(tSerializacion));

    administradorBuffering = new AdministradorBufferingBSharp(tSerializacion,
            tamanoComponente);

    administradorBuffering->setIndice(this);

    controladorAlmacenamiento = new ControladorAlmacenamientoBSharp(
            (*administradorBuffering), tSerializacion, tamanoComponente,
            *primario, wrapperVacio, *secundario, wrapperBuffer);

}

IndiceBSharp::IndiceBSharp(const string &ruta, const string &nombre,
        const TipoSerializacion &tSerializacion)
{
    this-> tSerializacion = tSerializacion;
    string rutaNueva = ruta + "indiceBSharp/";

    primario = new Archivo(rutaNueva, nombre, tSerializacion);

    stringstream metadata;
    primario->leerMetaData(metadata);
    unsigned long int tamanoComponente = 0;
    hidratacionDigito(metadata, tSerializacion, tamanoComponente);
    unsigned long int alturaBSharp = 0;
    hidratacionDigito(metadata, tSerializacion, alturaBSharp);

    WrapperComponente wrapperVacio;
    WrapperBufferBSharp wrapperBuffer(0);

    secundario = new Buffer(tamanoComponente + OFFSET_BUFFER
            + wrapperBuffer.getTamanoMetadata(tSerializacion), tSerializacion,
            (4 * tamanoComponente) / 3 + 1 + OFFSET_BUFFER
                    + wrapperBuffer.getTamanoMetadata(tSerializacion));
    stringstream stream;
    wrapperBuffer.serializar(stream, tSerializacion);
    secundario->guardarDato(stream);

    administradorBuffering = new AdministradorBufferingBSharp(tSerializacion,
            tamanoComponente);
    administradorBuffering->setIndice(this);

    controladorAlmacenamiento = new ControladorAlmacenamientoBSharp(
            (*administradorBuffering), tSerializacion, tamanoComponente,
            alturaBSharp);

    administradorBuffering->agregarComponenteAlterado(0);
}

IndiceBSharp::~IndiceBSharp()
{
    stringstream metadata;
    serializacionDigito(metadata, tSerializacion, primario->getTamano());
    serializacionDigito(metadata, tSerializacion,
            controladorAlmacenamiento->getAlturaBSharp());
    primario->actualizarMetadata(metadata);

    delete secundario;
    delete primario;

    delete controladorAlmacenamiento;
    delete administradorBuffering;
}

bool IndiceBSharp::eliminar(const Clave &clave)
{
    DatoBSharp dato(clave, 0, tSerializacion);
    controladorAlmacenamiento->setDatoAuxiliar(dato);
    administradorBuffering->eliminarDato(clave, dato);

    return dato.getPosicion() != 0;
}
void IndiceBSharp::insertar(const Clave & clave,
        const unsigned long int &posicionBloque)
{
    DatoBSharp dato(clave, posicionBloque, tSerializacion);
    controladorAlmacenamiento->setDatoAuxiliar(dato);
    administradorBuffering->guardarDato(dato, clave);
}

bool IndiceBSharp::buscar(const Clave &clave, unsigned long int &posicionBloque) const
{
    DatoBSharp dato(clave, 0, tSerializacion);
    controladorAlmacenamiento->setDatoAuxiliar(dato);
    bool resultado = administradorBuffering->recuperarDato(clave, dato);

    posicionBloque = dato.getPosicion();

    return resultado;
}

bool IndiceBSharp::getSiguienteClave(const Clave &clave, Clave*& claveSiguiente) const
{
    unsigned long int posicionBloque = this->administradorBuffering->busquedaInexacta(clave);

    bool estaEnSecundario = false;
    BloqueBSharp *nodo = this->administradorBuffering->buscarBloque(posicionBloque, estaEnSecundario);

    Clave* claveMaxima = nodo->claveMaxima();

    if((*claveMaxima) == clave){
        nodo = this->administradorBuffering->buscarBloque(nodo->getDireccion(), estaEnSecundario);

        claveSiguiente = nodo->claveMinima()->clonar();
        return true;

    } else {
    	return nodo->getClaveSiguiente(clave, claveSiguiente);
    }

    claveSiguiente = NULL;
    return false;
}

void IndiceBSharp::imprimirArbol() const
{
    this->controladorAlmacenamiento->imprimirArbol(*this->primario, 0);
}

}
